Make GtkNotebook able to drop detached tabs anywhere. Bug #360225.
authorCarlos Garnacho <carlosg@gnome.org>
Thu, 28 Dec 2006 16:38:49 +0000 (16:38 +0000)
committerCarlos Garnacho <carlosg@src.gnome.org>
Thu, 28 Dec 2006 16:38:49 +0000 (16:38 +0000)
2006-12-28  Carlos Garnacho  <carlosg@gnome.org>

        Make GtkNotebook able to drop detached tabs anywhere. Bug #360225.

        * gtk/gtkwidget.c (gtk_widget_class_init): add "drag-failed" signal.
        * gtk/gtkmarshalers.list: add new marshaler definition.
        * gtk/gtkenums.h: add GtkDragResult enum.

        * gtk/gtkdnd.c (gtk_drag_drop_finished): emit "drag-failed" if DND
        operation wasn't successful.
        (_gtk_drag_source_handle_event) (gtk_drag_drop)
        (gtk_drag_selection_get) (gtk_drag_cancel) (gtk_drag_key_cb)
        (gtk_drag_grab_broken_event_cb) (gtk_drag_grab_notify_cb)
        (gtk_drag_button_release_cb) (gtk_drag_abort_timeout): tell
        gtk_drag_drop_finished() the operation result.

        * gtk/gtknotebook.c (gtk_notebook_drag_failed): new function.
        (gtk_notebook_drag_data_get): do not call window creation hook here.
        (gtk_notebook_init): do not set "application/x-rootwindow-drop"
        target, instead connect to "drag-failed".
        (gtk_notebook_draw_focus): fix potential crasher if cur_page is NULL.

ChangeLog
gtk/gtkdnd.c
gtk/gtkenums.h
gtk/gtkmarshalers.list
gtk/gtknotebook.c
gtk/gtkwidget.c

index b1142860ec92eacffdd4ea6a1a5297db8c0bbff4..d66beb1dda700211a0fc53b4e4253c50edc26180 100644 (file)
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,25 @@
+2006-12-28  Carlos Garnacho  <carlosg@gnome.org>
+
+       Make GtkNotebook able to drop detached tabs anywhere. Bug #360225.
+
+       * gtk/gtkwidget.c (gtk_widget_class_init): add "drag-failed" signal.
+       * gtk/gtkmarshalers.list: add new marshaler definition.
+       * gtk/gtkenums.h: add GtkDragResult enum.
+
+       * gtk/gtkdnd.c (gtk_drag_drop_finished): emit "drag-failed" if DND
+       operation wasn't successful.
+       (_gtk_drag_source_handle_event) (gtk_drag_drop)
+       (gtk_drag_selection_get) (gtk_drag_cancel) (gtk_drag_key_cb)
+       (gtk_drag_grab_broken_event_cb) (gtk_drag_grab_notify_cb)
+       (gtk_drag_button_release_cb) (gtk_drag_abort_timeout): tell
+       gtk_drag_drop_finished() the operation result.
+
+       * gtk/gtknotebook.c (gtk_notebook_drag_failed): new function.
+       (gtk_notebook_drag_data_get): do not call window creation hook here.
+       (gtk_notebook_init): do not set "application/x-rootwindow-drop"
+       target, instead connect to "drag-failed".
+       (gtk_notebook_draw_focus): fix potential crasher if cur_page is NULL.
+
 2006-12-28  Mikael Hallendal  <micke@imendio.com>
 
        * gdk/quartz/gdkevents-quartz.c: Factored out the event loop 
index 939f5f2cde6afc9950aaaf34a0ec889d10707d2e..a30da5e5e17c8d326cb13fb609f609334ef8d854 100644 (file)
@@ -252,9 +252,10 @@ static void gtk_drag_source_release_selections (GtkDragSourceInfo *info,
 static void gtk_drag_drop                      (GtkDragSourceInfo *info,
                                                guint32            time);
 static void gtk_drag_drop_finished             (GtkDragSourceInfo *info,
-                                               gboolean           success,
+                                               GtkDragResult      result,
                                                guint              time);
 static void gtk_drag_cancel                    (GtkDragSourceInfo *info,
+                                               GtkDragResult      result,
                                                guint32            time);
 
 static gboolean gtk_drag_source_event_cb       (GtkWidget         *widget,
@@ -2974,7 +2975,7 @@ set_icon_stock_pixbuf (GdkDragContext    *context,
   gtk_widget_pop_colormap ();
 
   gtk_widget_set_events (window, GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK);
-  gtk_widget_set_app_paintable (GTK_WIDGET (window), TRUE);
+  gtk_widget_set_app_paintable (window, TRUE);
 
   if (stock_id)
     {
@@ -3333,7 +3334,7 @@ _gtk_drag_source_handle_event (GtkWidget *widget,
       break;
       
     case GDK_DROP_FINISHED:
-      gtk_drag_drop_finished (info, TRUE, event->dnd.time);
+      gtk_drag_drop_finished (info, GTK_DRAG_RESULT_SUCCESS, event->dnd.time);
       break;
     default:
       g_assert_not_reached ();
@@ -3413,9 +3414,12 @@ gtk_drag_source_check_selection (GtkDragSourceInfo *info,
 
 static void
 gtk_drag_drop_finished (GtkDragSourceInfo *info,
-                       gboolean           success,
+                       GtkDragResult      result,
                        guint              time)
 {
+  gboolean success;
+
+  success = (result == GTK_DRAG_RESULT_SUCCESS);
   gtk_drag_source_release_selections (info, time); 
 
   if (info->proxy_dest)
@@ -3427,6 +3431,10 @@ gtk_drag_drop_finished (GtkDragSourceInfo *info,
     }
   else
     {
+      if (!success)
+       g_signal_emit_by_name (info->widget, "drag_failed",
+                              info->context, result, &success);
+
       if (success)
        {
          gtk_drag_source_info_destroy (info);
@@ -3518,12 +3526,12 @@ gtk_drag_drop (GtkDragSourceInfo *info,
                                     time);
              
              /* FIXME: Should we check for length >= 0 here? */
-             gtk_drag_drop_finished (info, TRUE, time);
+             gtk_drag_drop_finished (info, GTK_DRAG_RESULT_SUCCESS, time);
              return;
            }
          tmp_list = tmp_list->next;
        }
-      gtk_drag_drop_finished (info, FALSE, time);
+      gtk_drag_drop_finished (info, GTK_DRAG_RESULT_NO_TARGET, time);
     }
   else
     {
@@ -3635,11 +3643,11 @@ gtk_drag_selection_get (GtkWidget        *widget,
       gtk_selection_data_set (selection_data, null_atom, 8, NULL, 0);
       break;
     case TARGET_MOTIF_SUCCESS:
-      gtk_drag_drop_finished (info, TRUE, time);
+      gtk_drag_drop_finished (info, GTK_DRAG_RESULT_SUCCESS, time);
       gtk_selection_data_set (selection_data, null_atom, 8, NULL, 0);
       break;
     case TARGET_MOTIF_FAILURE:
-      gtk_drag_drop_finished (info, FALSE, time);
+      gtk_drag_drop_finished (info, GTK_DRAG_RESULT_NO_TARGET, time);
       gtk_selection_data_set (selection_data, null_atom, 8, NULL, 0);
       break;
     default:
@@ -3975,11 +3983,11 @@ gtk_drag_end (GtkDragSourceInfo *info, guint32 time)
  *************************************************************/
 
 static void
-gtk_drag_cancel (GtkDragSourceInfo *info, guint32 time)
+gtk_drag_cancel (GtkDragSourceInfo *info, GtkDragResult result, guint32 time)
 {
   gtk_drag_end (info, time);
   gdk_drag_abort (info->context, time);
-  gtk_drag_drop_finished (info, FALSE, time);
+  gtk_drag_drop_finished (info, result, time);
 }
 
 /*************************************************************
@@ -4044,7 +4052,7 @@ gtk_drag_key_cb (GtkWidget         *widget,
       switch (event->keyval)
        {
        case GDK_Escape:
-         gtk_drag_cancel (info, event->time);
+         gtk_drag_cancel (info, GTK_DRAG_RESULT_USER_CANCELLED, event->time);
          return TRUE;
 
        case GDK_space:
@@ -4118,7 +4126,7 @@ gtk_drag_grab_broken_event_cb (GtkWidget          *widget,
       || event->grab_window == info->ipc_widget->window)
     return FALSE;
 
-  gtk_drag_cancel (info, gtk_get_current_event_time ());
+  gtk_drag_cancel (info, GTK_DRAG_RESULT_GRAB_BROKEN, gtk_get_current_event_time ());
   return TRUE;
 }
 
@@ -4134,7 +4142,7 @@ gtk_drag_grab_notify_cb (GtkWidget        *widget,
       /* We have to block callbacks to avoid recursion here, because
         gtk_drag_cancel calls gtk_grab_remove (via gtk_drag_end) */
       g_signal_handlers_block_by_func (widget, gtk_drag_grab_notify_cb, data);
-      gtk_drag_cancel (info, gtk_get_current_event_time ());
+      gtk_drag_cancel (info, GTK_DRAG_RESULT_GRAB_BROKEN, gtk_get_current_event_time ());
       g_signal_handlers_unblock_by_func (widget, gtk_drag_grab_notify_cb, data);
     }
 }
@@ -4165,7 +4173,7 @@ gtk_drag_button_release_cb (GtkWidget      *widget,
     }
   else
     {
-      gtk_drag_cancel (info, event->time);
+      gtk_drag_cancel (info, GTK_DRAG_RESULT_NO_TARGET, event->time);
     }
 
   return TRUE;
@@ -4181,7 +4189,7 @@ gtk_drag_abort_timeout (gpointer data)
     time = info->proxy_dest->proxy_drop_time;
 
   info->drop_timeout = 0;
-  gtk_drag_drop_finished (info, FALSE, time);
+  gtk_drag_drop_finished (info, GTK_DRAG_RESULT_TIMEOUT_EXPIRED, time);
   
   return FALSE;
 }
index 85bfd38f8236c49b699971b366880188fe21f3da..a3a5e068ce56d894f3c6379b8503905be6b89f69 100644 (file)
@@ -514,6 +514,15 @@ typedef enum {
   GTK_TREE_VIEW_GRID_LINES_BOTH
 } GtkTreeViewGridLines;
 
+typedef enum {
+  GTK_DRAG_RESULT_SUCCESS,
+  GTK_DRAG_RESULT_NO_TARGET,
+  GTK_DRAG_RESULT_USER_CANCELLED,
+  GTK_DRAG_RESULT_TIMEOUT_EXPIRED,
+  GTK_DRAG_RESULT_GRAB_BROKEN,
+  GTK_DRAG_RESULT_ERROR
+} GtkDragResult;
+
 G_END_DECLS
 
 #endif /* __GTK_ENUMS_H__ */
index c11f42b741c5f0f426a3720ef9f25bc109e7bab6..c1510ec2fe0b635d01952a0e7686eb543f994c6c 100644 (file)
@@ -34,6 +34,7 @@ BOOLEAN:OBJECT,BOXED
 BOOLEAN:OBJECT,BOXED,BOXED
 BOOLEAN:OBJECT,OBJECT,OBJECT
 BOOLEAN:OBJECT,STRING,STRING
+BOOLEAN:OBJECT,ENUM
 BOOLEAN:INT
 BOOLEAN:INT,INT
 BOOLEAN:INT,INT,INT
index 85d9953fd1ec9e14c7ea9c7fb0eda96ffae729ec..23abb5921774eab1b0e5c79f0f1d7f4a62924f0d 100644 (file)
@@ -184,13 +184,8 @@ struct _GtkNotebookPrivate
   guint has_scrolled   : 1;
 };
 
-static const GtkTargetEntry notebook_source_targets [] = {
+static const GtkTargetEntry notebook_targets [] = {
   { "GTK_NOTEBOOK_TAB", GTK_TARGET_SAME_APP, 0 },
-  { "application/x-rootwindow-drop", 0, 1 }
-};
-
-static const GtkTargetEntry notebook_dest_targets[] = {
-  { "GTK_NOTEBOOK_TAB", GTK_TARGET_SAME_APP, 0 }
 };
 
 #ifdef G_DISABLE_CHECKS
@@ -267,6 +262,10 @@ static void gtk_notebook_drag_begin          (GtkWidget        *widget,
                                              GdkDragContext   *context);
 static void gtk_notebook_drag_end            (GtkWidget        *widget,
                                              GdkDragContext   *context);
+static gboolean gtk_notebook_drag_failed     (GtkWidget        *widget,
+                                             GdkDragContext   *context,
+                                             GtkDragResult     result,
+                                             gpointer          data);
 static gboolean gtk_notebook_drag_motion     (GtkWidget        *widget,
                                              GdkDragContext   *context,
                                              gint              x,
@@ -406,6 +405,11 @@ static gboolean focus_child_in (GtkNotebook      *notebook,
                                GtkDirectionType  direction);
 
 static void stop_scrolling (GtkNotebook *notebook);
+static void do_detach_tab  (GtkNotebook *from,
+                           GtkNotebook *to,
+                           GtkWidget   *child,
+                           gint         x,
+                           gint         y);
 
 
 static GtkNotebookWindowCreationFunc window_creation_hook = NULL;
@@ -1000,8 +1004,8 @@ gtk_notebook_init (GtkNotebook *notebook)
   priv->pressed_button = -1;
   priv->dnd_timer = 0;
   priv->switch_tab_timer = 0;
-  priv->source_targets = gtk_target_list_new (notebook_source_targets,
-                                             G_N_ELEMENTS (notebook_source_targets));
+  priv->source_targets = gtk_target_list_new (notebook_targets,
+                                             G_N_ELEMENTS (notebook_targets));
   priv->operation = DRAG_OPERATION_NONE;
   priv->detached_tab = NULL;
   priv->during_detach = FALSE;
@@ -1009,9 +1013,12 @@ gtk_notebook_init (GtkNotebook *notebook)
 
   gtk_drag_dest_set (GTK_WIDGET (notebook),
                     GTK_DEST_DEFAULT_MOTION | GTK_DEST_DEFAULT_DROP,
-                    notebook_dest_targets, G_N_ELEMENTS (notebook_dest_targets),
+                    notebook_targets, G_N_ELEMENTS (notebook_targets),
                      GDK_ACTION_MOVE);
 
+  g_signal_connect (G_OBJECT (notebook), "drag-failed",
+                   G_CALLBACK (gtk_notebook_drag_failed), NULL);
+
   gtk_drag_dest_set_track_motion (GTK_WIDGET (notebook), TRUE);
 }
 
@@ -1470,6 +1477,7 @@ gtk_notebook_get_property (GObject         *object,
  * gtk_notebook_style_set
  * gtk_notebook_drag_begin
  * gtk_notebook_drag_end
+ * gtk_notebook_drag_failed
  * gtk_notebook_drag_motion
  * gtk_notebook_drag_drop
  * gtk_notebook_drag_data_get
@@ -2975,8 +2983,8 @@ gtk_notebook_draw_focus (GtkWidget      *widget,
   GtkNotebook *notebook = GTK_NOTEBOOK (widget);
 
   if (GTK_WIDGET_HAS_FOCUS (widget) && GTK_WIDGET_DRAWABLE (widget) &&
-      notebook->cur_page->tab_label->window == event->window &&
-      notebook->show_tabs && notebook->cur_page)
+      notebook->show_tabs && notebook->cur_page &&
+      notebook->cur_page->tab_label->window == event->window)
     {
       GtkNotebookPage *page;
       GdkRectangle area;
@@ -3106,6 +3114,38 @@ gtk_notebook_drag_end (GtkWidget      *widget,
   priv->operation = DRAG_OPERATION_NONE;
 }
 
+static gboolean
+gtk_notebook_drag_failed (GtkWidget      *widget,
+                         GdkDragContext *context,
+                         GtkDragResult   result,
+                         gpointer        data)
+{
+  if (result == GTK_DRAG_RESULT_NO_TARGET)
+    {
+      GtkNotebookPrivate *priv;
+      GtkNotebook *notebook, *dest_notebook;
+      GdkDisplay *display;
+      gint x, y;
+
+      notebook = GTK_NOTEBOOK (widget);
+      priv = GTK_NOTEBOOK_GET_PRIVATE (notebook);
+
+      display = gtk_widget_get_display (widget);
+      gdk_display_get_pointer (display, NULL, &x, &y, NULL);
+
+      dest_notebook = (* window_creation_hook) (notebook,
+                                               priv->detached_tab->child,
+                                               x, y,
+                                               window_creation_hook_data);
+      if (dest_notebook)
+       do_detach_tab (notebook, dest_notebook, priv->detached_tab->child, 0, 0);
+
+      return TRUE;
+    }
+
+  return FALSE;
+}
+
 static gboolean
 gtk_notebook_switch_tab_timeout (gpointer data)
 {
@@ -3332,40 +3372,18 @@ gtk_notebook_drag_data_get (GtkWidget        *widget,
                            guint             info,
                            guint             time)
 {
-  GtkNotebook *dest_notebook, *notebook;
   GtkNotebookPrivate *priv;
 
-  if (data->target != gdk_atom_intern_static_string ("GTK_NOTEBOOK_TAB") &&
-      (data->target != gdk_atom_intern_static_string ("application/x-rootwindow-drop") ||
-       !window_creation_hook))
-    return;
-
-  notebook = GTK_NOTEBOOK (widget);
-  priv = GTK_NOTEBOOK_GET_PRIVATE (notebook);
-
   if (data->target == gdk_atom_intern_static_string ("GTK_NOTEBOOK_TAB"))
     {
+      priv = GTK_NOTEBOOK_GET_PRIVATE (widget);
+
       gtk_selection_data_set (data,
                              data->target,
                              8,
                              (void*) &priv->detached_tab->child,
                              sizeof (gpointer));
     }
-  else
-    {
-      GdkDisplay *display;
-      gint x, y;
-
-      display = gtk_widget_get_display (widget);
-      gdk_display_get_pointer (display, NULL, &x, &y, NULL);
-
-      dest_notebook = (* window_creation_hook) (notebook,
-                                               priv->detached_tab->child,
-                                               x, y,
-                                               window_creation_hook_data);
-      if (dest_notebook)
-       do_detach_tab (notebook, dest_notebook, priv->detached_tab->child, 0, 0);
-    }
 }
 
 static void
index f021bf028dbd2276887e918488db30f9fdaecff4..7a727433d87f6e1b1d6bd6539bb3833c2f4d507a 100644 (file)
@@ -122,6 +122,7 @@ enum {
   GRAB_BROKEN,
   COMPOSITED_CHANGED,
   KEYNAV_FAILED,
+  DRAG_FAILED,
   LAST_SIGNAL
 };
 
@@ -1124,6 +1125,32 @@ gtk_widget_class_init (GtkWidgetClass *klass)
                  G_TYPE_NONE, 1,
                  GDK_TYPE_DRAG_CONTEXT);
 
+  /**
+   * GtkWidget::drag-failed:
+   * @widget: the object which received the signal.
+   * @drag_context: the drag context.
+   * @result: the result of the drag operation.
+   *
+   * The ::drag-failed signal is emitted on the drag source when a drag has
+   * failed. The signal handler may hook custom code to handle a failed DND
+   * operation based on the type of error, it returns %TRUE is the failure has
+   * been already handled (not showing the default "drag operation failed"
+   * animation), otherwise it returns %FALSE.
+   *
+   * Return value: %TRUE if the failed drag operation has been already handled.
+   *
+   * Since: 2.12
+   */
+  widget_signals[DRAG_FAILED] =
+    g_signal_new (I_("drag_failed"),
+                 G_TYPE_FROM_CLASS (gobject_class),
+                 G_SIGNAL_RUN_LAST,
+                 0, _gtk_boolean_handled_accumulator, NULL,
+                 _gtk_marshal_BOOLEAN__OBJECT_ENUM,
+                 G_TYPE_BOOLEAN, 2,
+                 GDK_TYPE_DRAG_CONTEXT,
+                 GTK_TYPE_DRAG_RESULT);
+
   /**
    * GtkWidget::drag-motion:
    * @widget: the object which received the signal.